#include "dir.h"

static const char *dir_page =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
"<head>\n"
/* css */
"  <style type='text/css'>\n"
"/*<![CDATA[*/\n"
"  body { background-color: white; }\n"
"  #content { border:2px solid #000000; margin:5em auto 10px auto; font-family:Georgia, Times, serifs; width:600px; padding: 30px 50px; }\n"
"  #top { font-weight:bold; padding: 7px 0; }\n"
"  div.ico { width:16px; height:16px; float:left; }\n"
"  div.filename { width:300px; float:left; text-align:left; margin-left:10px; }\n"
"  div.modified { width:150px; float:left; text-align:left; }\n"
"  div.size { width:80px; float:right; text-align:right; }\n"
"  .clear { clear:both; }\n"
"  .bar { border-top:#000000 2px solid; margin-bottom:5px; clear:both; }\n"
/* arrow */
"  .arrow { background-image: url(); }\n"
/* texts */
"  .file { background-image: url(); }\n"
"  .folder { background-image: url(); }\n"
"  .html { background-image: url(); }\n"
"  .htm { background-image: url(); }\n"
"  .css { background-image: url(); }\n"
"  .js { background-image: url(); }\n"
"  .xhtml { background-image: url(); }\n"
/* images */
"  .gif { background-image: url(); }\n"
"  .jpg { background-image: url(); }\n"
"  .jpeg { background-image: url(); }\n"
"  .png { background-image: url(); }\n"
/* audio */
"  .mp3 { background-image: url(); }\n"
"  .ogg { background-image: url(); }\n"
/* archive */
"  .rar { background-image: url(); }\n"
"  .bz2 { background-image: url(); }\n"
"  .zip { background-image: url(); }\n"
"  .gz { background-image: url(); }\n"
"  .tar { background-image: url(); }\n"
/* other */
"  .pdf { background-image: url(); }\n"
"/*]]>*/\n"
"  </style>\n"
"  <meta http-equiv='Content-Type' content='application/xhtml+xml' />\n\n"
/* directory we are indexing */
"  <title>Index of %s</title>\n"
"</head>\n\n"
"<body>\n"
"	<div id='content'>\n"
/* directory we are indexing */
"		<h2 style='float: left'>Index of %s</h2>\n"
"       <span style='font-style: italic; float:right'>" PROGRAM " " VERSION "</span>\n"
"       <div class='clear'></div>\n"
"		<div id='top'>\n"
"			<div class='ico'></div>\n"
"			<div class='filename'>Filename</div>\n"
"			<div class='modified'>Last Modified</div>\n"
"			<div class='size'>Size</div>\n"
"		</div>\n"
"		<div class='bar'></div>\n\n"
/* dir_entry listing goes here */
"%s"
"	</div>\n"
"</body>\n"
"</html>\n";

static const char *dir_entry =
"		<div>\n"
"			<div class='ico %s'></div>\n" /* base64 icon */
"			<div class='filename'><a href='%s'>%.30s</a></div>\n" /* file name, file name */
"			<div class='modified'>%s</div>\n" /* date last modified */
"			<div class='size'>%s %s</div>\n" /* size of file and suffix */
"		</div>\n"
"		<div class='clear'></div>\n\n";

static const struct {
	char *ext;
} icons[] = {
	{"html"},
	{"htm"},
	{"css"},
	{"js"},
	{"xhtml"},
	{"gif"},
	{"jpg"},
	{"jpeg"},
	{"png"},
	{"mp3"},
	{"ogg"},
	{"rar"},
	{"bz2"},
	{"zip"},
	{"gz"},
	{"tar"},
	{"pdf"},
	{0}
};

int make_dir_list(connection *ret) {
	char buf[8 * KILOBYTE] = {0};
	struct stat stattmp;
	struct dirent **filelist;
	int filecount = 0;
	char dir[8 * KILOBYTE] = {0};
	char **entries = 0;

	/* FIX THIS LATER */
	char *filenamebuf;
	char iconbuf[10] = {0};
	char timestampbuf[256] = {0};
	char sizebuf[50];
	float bytes = 0;
	const char *suffix = 0;
	struct tm timetmp;
	const char *timestr = "%d-%b-%Y %H:%M";

	memset(&timetmp, 0, sizeof(struct tm));

	int entrieslen = 0;

	/* so, the basic idea is as follows:
	 * 1. scan the directory
	 * 2. stat the file for infos
	 * 3. grab the filename
	 * 4. get an icon based on filename/type
	 * 5. get the size
	 * 6. get the last modified timestamp
	 * 7. pop it in dir_entry
	 * 8. put those in dir_page
	 * 9. return all that shit */

	// always start relative
	strcpy(dir, "./");
	url_decode(buf, ret->req.resource+1);
	strcat(dir, buf);

#ifdef _GNU_SOURCE
  int (*compar)(const struct dirent **, const struct dirent **) = versionsort;
#else
  int (*compar)(const void*, const void*) = alphasort;
#endif

	// filter out ".." if we are in /
	if( strncmp(dir, "./", 3) ) {
		filecount = scandir(dir, &filelist, yesparent, compar);
	} else {
		filecount = scandir(dir, &filelist, noparent, compar);
	}

	if(filecount >= 0) {
		int cnt;
		entries = malloc(filecount * sizeof(*entries));
		memset(entries, 0, sizeof(entries));

		for(cnt = 0; cnt < filecount; ++cnt) {
			char statbuf[1024];
			strcpy(statbuf, dir);
			strcat(statbuf, filelist[cnt]->d_name);
			stat(statbuf, &stattmp);

			// get file name
			filenamebuf = strdup(filelist[cnt]->d_name);
			filenamebuf = realloc(filenamebuf, strlen(filenamebuf) + 5);

			if( S_ISREG(stattmp.st_mode) ) {
				strcpy(iconbuf, icon_from_fname(filenamebuf) );
				// set proper suffix
				suffix = get_suffx_from_size(stattmp.st_size, &bytes);
				if(bytes==floor(bytes)) {
					sprintf(sizebuf, "%.0f", bytes);
				} else {
					sprintf(sizebuf, "%.1f", bytes);
				}
			} else if(S_ISDIR(stattmp.st_mode)) {
				strcpy(iconbuf, "folder");
				strcpy(sizebuf, "-");
				strcat(filenamebuf, "/");
				suffix = "";
			} else {
				/* isn't a regular file or folder... wtf is it? */
				strcpy(iconbuf, "file");
				strcpy(sizebuf, "-");
			}

			/* 6. gimme a timestamp */
			gmtime_r(&stattmp.st_mtime, &timetmp);
			strftime(timestampbuf, 256, timestr, &timetmp);

			/* 7. put in buffer, order = icon, filename, time modified, size */
			/* put all the directory data into the div tags */
			int len;
			if( strcmp(filenamebuf, "../") == 0) {
				len = asprintf(&entries[cnt], dir_entry, "arrow", filenamebuf, "Parent Directory", "", sizebuf, "");
			} else {
				len = asprintf(&entries[cnt], dir_entry, iconbuf, filenamebuf, filenamebuf, timestampbuf, sizebuf, suffix);
			}
			entrieslen += len + 1;

			/* free the linked list and file name buffer */
			free(filelist[cnt]);
			free(filenamebuf);

		}

		char *entriestotal = malloc(entrieslen * sizeof(*entriestotal));
		/* danke BhaaL */
		memset(entriestotal, 0, entrieslen);

		for(cnt = 0; cnt < filecount; ++cnt) {
			strcat(entriestotal, entries[cnt]);
			free(entries[cnt]);
		}
		free(entries);
		free(filelist);

		ret->response.content_size = asprintf(&ret->response.data, dir_page, dir+1, dir+1, entriestotal);
		if(ret->response.content_size <= 0) {
			/* asprintf went bad */
			ERROR("asprintf(): %s\n", strerror(errno));
			ret->response.status = HTTP_INTERNAL_ERROR;
			error_code_to_data( &(ret->response) );
			return -1;
		}

		free(entriestotal);

		/* our directory listings are XHTML 1.0 Strict compliant, damnit :(
		* sigh, send as text/html to compensate for IE and other crap browsers..
		* even though that isn't ideal and is read as tag soup.. */
		strcpy(ret->response.mimetype, "text/html");

		return 0;
	} else {
		ERROR("scandir failed: %s\n", strerror(errno));
		ret->response.status = HTTP_NOT_FOUND;
		error_code_to_data( &(ret->response) );
		return -1;
	}

	// something odd happened
	return -1;
}

const char *icon_from_fname(const char *filename) {
	int i, len_ico, len = strlen(filename);

	for(i=0;icons[i].ext != 0;i++) {
		len_ico = strlen(icons[i].ext);
		if( strncmp(&filename[len-len_ico], icons[i].ext, len_ico) == 0) {
			return icons[i].ext;
		}
	}

	// no icon. send a blank one.
	return "file";
}

const char *get_suffx_from_size(unsigned long bytes, float *floaty) {
	static char const *suffix[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", 0};
	int i;
	*floaty = bytes;

	for(i = 0; bytes > 1024; ++i) {
		bytes /= 1024;
		*floaty /= 1024;
		}
	if(suffix[i] == 0)
		return suffix[6];

	return suffix[i];
}

int exist_index(connection *ret) {

	DIR *dp;
	struct dirent *ep;
	char dir[FILENAME_MAX] = {0};
	char buf[FILENAME_MAX] = {0};
	strcpy(dir, "./");
	url_decode(buf, ret->req.resource+1);
	strcat(dir, buf);

	dp = opendir (dir);
	if (dp != NULL)
	{
		while ( (ep = readdir (dp))  ) {
			if( strcmp(ep->d_name, "index.xhtml") == 0) {
				strcpy(buf, dir);
				strcat(buf, ep->d_name);
				free(ret->req.resource);
				ret->req.resource = strdup(buf+1);
				closedir (dp);
				return 1;
			} else if( strcmp(ep->d_name, "index.html") == 0) {
				strcpy(buf, dir);
				strcat(buf, ep->d_name);
				free(ret->req.resource);
				ret->req.resource = strdup(buf+1);
				closedir (dp);
				return 1;
			} else if( strcmp(ep->d_name, "index.htm") == 0) {
				strcpy(buf, dir);
				strcat(buf, ep->d_name);
				free(ret->req.resource);
				ret->req.resource = strdup(buf+1);
				closedir (dp);
				return 1;
			}
		}

		closedir (dp);
	} else {
		ERROR("failed reading directory %s\n", dir);
	}

	return 0;
}

int noparent(const struct dirent *entry) {
	if( strncmp(entry->d_name, "..", 3) == 0) return 0;
	if( strncmp(entry->d_name, ".", 2) == 0) return 0;
	return 1;
}

int yesparent(const struct dirent *entry) {
	if( strncmp(entry->d_name, ".", 3) == 0) return 0;
	return 1;
}
